package com.novax.ex.activity.provider.controller.inner;

import com.novax.ex.activity.infrastructure.entity.*;
import com.novax.ex.activity.open.api.inner.IeoInnerConfigApi;
import com.novax.ex.activity.open.model.query.IeoConfigQuery;
import com.novax.ex.activity.open.model.query.OrderInnerQuery;
import com.novax.ex.activity.open.model.request.*;
import com.novax.ex.activity.open.model.response.*;
import com.novax.ex.activity.provider.handler.provider.IeoProviderService;
import com.novax.ex.common.base.BaseEntity;
import com.novax.ex.common.constant.InternationalCode;
import com.novax.ex.activity.provider.controller.BaseActivityController;
import com.novax.ex.activity.provider.service.*;
import com.novax.ex.common.results.ReturnResult;
import com.novax.ex.common.util.CopyUtils;
import com.novax.ex.common.util.DateUtil;
import com.novax.ex.common.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RestController;

import jakarta.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description 后台活动配置Controller
 * @Author Cody
 * @Date 2022/12/01/18:09
 */
@Slf4j
@RestController
public class IeoConfigController extends BaseActivityController implements IeoInnerConfigApi {

    @Resource
    private IeoConfigService ieoConfigService;

    @Resource
    private IeoCurrencyConfigService ieoCurrencyConfigService;

    @Resource
    private IeoContentService ieoContentService;

    @Resource
    private IeoVipLimitService ieoVipLimitService;

    @Resource
    private IeoOrderService ieoOrderService;

    @Resource
    private IeoProviderService ieoProviderService;

    @Override
    public ReturnResult<PageUtil<IeoConfigResponse>> page(IeoConfigQuery query) {
        PageUtil<IeoConfigResponse> byPage = ieoConfigService.findByPage(query);
        return ReturnResult.success(byPage);
    }

    @Override
    public ReturnResult<?> add(String language, IeoConfigRequest configRequest, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return ReturnResult.fail(getInternational(language, InternationalCode.LACK_VALUE));
        }
        // 币种转小写
        configRequest.setCurrency(currencyToLowerCase(configRequest.getCurrency()));
        // 活动开始时间结束时间解冻时间校验
        Date startTime = configRequest.getStartTime();
        Date endTime = configRequest.getEndTime();
        if(startTime.before(endTime)){
            // 创建币种活动后自动生成vip各等级限购数
            boolean res = ieoConfigService.addAndBatchInsertVipLimit(configRequest);
            // 活动配置添加成功且vip默认限购配置添加成功
            return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                    : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }else {
            return ReturnResult.fail(getInternational(language, InternationalCode.THAW_TIME_AFTER_END_TIME));
        }
    }

    @Override
    public ReturnResult<?> modify(String language, Long ieoId, IeoConfigRequest configRequest) {
        if (Objects.isNull(ieoId)) {
            return ReturnResult.fail(getInternational(language, InternationalCode.LACK_VALUE));
        }
        IeoConfig ieoConfig = ieoConfigService.find(ieoId);
        if(Objects.isNull(ieoConfig)){
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_FOUND));
        }

        // 在活动结束后可以更新涨幅
        Date configEndTime = ieoConfig.getEndTime();
        Integer increaseHistory = configRequest.getIncreaseHistory();
        if(Objects.nonNull(configEndTime) && configEndTime.before(new Date()) && Objects.nonNull(increaseHistory)){
            ieoConfig = CopyUtils.copyObject(configRequest, IeoConfig.class);
            ieoConfig.setId(ieoId);
            boolean res = ieoConfigService.modifySelective(ieoConfig);
            return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }


        // 如果已上架, 不可修改(除非下架, 修改前台倍率, 修改真实分配份数)
        Integer requestState = configRequest.getState();
        BigDecimal requestMultiple = configRequest.getMultiple();
        Integer requestSharePortion = configRequest.getSharePortion();
        Integer ieoConfigState = ieoConfig.getState();

        // 已发币后不可更改待分配的真实代币份数
        Integer unfreeze = ieoConfig.getUnfreeze();
        if(Objects.nonNull(unfreeze) && (unfreeze.equals(2) || unfreeze.equals(3) ) && Objects.nonNull(requestSharePortion)){
            return ReturnResult.fail(getInternational(language, InternationalCode.NOT_OPEN_OR_THAWED));
        }
        
        // 上架情况下
        if (Objects.nonNull(ieoConfigState) && ieoConfigState == 1){
            // 如果非下架请求 且 倍率字段 和 真实分配总份数都为空
            if((Objects.isNull(requestState) || requestState != 0)
                    && Objects.isNull(requestMultiple)
                    && Objects.isNull(requestSharePortion)){
                return ReturnResult.fail(getInternational(language, InternationalCode.CANNOT_BE_CHANGED));
            }
        }

        Date ieoConfigEndTime = ieoConfig.getEndTime();
        // 如果传递了时间参数(非必传) 则解锁时间需大于等于认购结束时间 且 开始时间不能晚于结束时间
        Date unlockTime = configRequest.getUnlockTime();
        Date endTime = configRequest.getEndTime();
        Date startTime = configRequest.getStartTime();
        if (unlockTime != null && endTime != null && startTime != null){
            if(unlockTime.equals(endTime) || unlockTime.before(endTime) || endTime.before(startTime)) {
                return ReturnResult.fail(getInternational(language,InternationalCode.THAW_TIME_AFTER_END_TIME));
            }
        }

        // 此次修改是否修改了待分配的真实代币份数, 修改了则重新计算每个订单的发币量
        boolean flag = false;
        // 真实分配字段修改校验
        if(Objects.nonNull(requestSharePortion)){
            flag = true;
            ieoConfig.setSharePortion(requestSharePortion);
            ieoConfig.setUpdateTime(new Date());
        }

        // 币种转小写
        configRequest.setCurrency(currencyToLowerCase(configRequest.getCurrency()));
        IeoConfig ieoConfigRequest = CopyUtils.copyObject(configRequest, IeoConfig.class);
        ieoConfigRequest.setUpdateTime(new Date());
        ieoConfigRequest.setId(ieoId);

        boolean res;

        // 结束时间2分钟后才可操作
        Date ieoConfigSecondEndTime = DateUtil.getNextMinute(DateUtil.discardSecond(ieoConfigEndTime), 2);
        if(flag && new Date().after(ieoConfigSecondEndTime)){
            res = ieoConfigService.modifyAndCalculate(ieoConfig);
        } else {
            res = ieoConfigService.modifySelective(ieoConfigRequest);
        }
        // 开启成功(0=未开启 1=开启) 发送MQ消息
        if(Objects.nonNull(requestState) && requestState == 1 && res){
            ieoProviderService.sendIeoCalculi(ieoId.toString(), ieoConfigEndTime);
        }
        return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    @Override
    public ReturnResult<List<IeoVipLimitResponse>> vipConfigs(Long ieoId) {
        List<IeoVipLimitResponse> resultList = CopyUtils.copyList(ieoVipLimitService.findByIeoId(ieoId), IeoVipLimitResponse.class);
        return ReturnResult.success(resultList);
    }

    @Override
    public ReturnResult<?> modifyVipConfig(String language, Long vipId, IeoVipRequest vipRequest, BindingResult bindingResult) {
        if(bindingResult.hasErrors()|| Objects.isNull(vipId)){
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }
        IeoVipLimit ieoVipLimit = new IeoVipLimit();
        ieoVipLimit.setLimit(vipRequest.getLimit());
        ieoVipLimit.setId(vipId);
        ieoVipLimit.setUpdateTime(new Date());
        boolean res = ieoVipLimitService.modifySelective(ieoVipLimit);
        return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    @Override
    public ReturnResult<List<IeoCurrencyConfigResponse>> currencyConfigs(Long ieoId) {
        List<IeoCurrencyConfigResponse> resultList = CopyUtils.copyList(ieoCurrencyConfigService.selectListByIeoId(ieoId), IeoCurrencyConfigResponse.class);
        return ReturnResult.success(resultList);
    }

    @Override
    public ReturnResult<?> addCurrencyConfig(String language, Long ieoId, IeoCurrencyRequest currencyRequest, BindingResult bindingResult) {
        if (Objects.isNull(ieoId) || bindingResult.hasErrors()) {
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }

        Integer isPricingCurrency = currencyRequest.getIsPricingCurrency();
        // 新增币种不能为计价币种(计价币种唯一且必须)
        if(Objects.nonNull(isPricingCurrency) && isPricingCurrency.equals(1)){
            return ReturnResult.fail(getInternational(language, InternationalCode.ADMIN_IDENTICAL_DATA));
        }

        // 币种转小写
        currencyRequest.setCurrency(currencyToLowerCase(currencyRequest.getCurrency()));
        IeoCurrencyConfig byIeoIdAndCurrency = ieoCurrencyConfigService.findByIeoIdAndCurrency(ieoId, currencyRequest.getCurrency());
        if (Objects.nonNull(byIeoIdAndCurrency)) {
            return ReturnResult.fail(getInternational(language, InternationalCode.ADMIN_IDENTICAL_DATA));
        }

        IeoCurrencyConfig ieoCurrencyConfig = CopyUtils.copyObject(currencyRequest, IeoCurrencyConfig.class);
        ieoCurrencyConfig.setIeoId(ieoId);
        ieoCurrencyConfig.setCreateTime(new Date());
        boolean res = ieoCurrencyConfigService.addSelective(ieoCurrencyConfig);
        return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.ADMIN_IDENTICAL_DATA));
    }

    @Override
    public ReturnResult<?> modifyCurrencyConfig(String language, Long currencyId, IeoCurrencyRequest currencyRequest, BindingResult bindingResult) {
        if (Objects.isNull(currencyId) || bindingResult.hasErrors()) {
            return ReturnResult.fail(getInternational(language,InternationalCode.LACK_VALUE));
        }

        BigDecimal usdtRate = currencyRequest.getUsdtRate();
        if(Objects.nonNull(usdtRate) && usdtRate.compareTo(BigDecimal.ZERO) <= 0){
            return ReturnResult.fail(getInternational(language,InternationalCode.UNREASONABLE));
        }

        IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigService.find(currencyId);
        if(Objects.isNull(ieoCurrencyConfig)){
            return ReturnResult.fail(getInternational(language,InternationalCode.NOT_FOUND));
        }

        // ieoId
        Long ieoId = ieoCurrencyConfig.getIeoId();
        IeoConfig ieoConfig = ieoConfigService.find(ieoId);
        // 认购结束不可修改
        if(ieoConfig.getUnfreeze() == 1 ){
            return ReturnResult.fail(getInternational(language,InternationalCode.EXCHANGE_RATE_CHANG_SUBSCRIPT));
        }

        // 获取计价币种
        IeoCurrencyConfig byIsPricingCurrency = ieoCurrencyConfigService.findByIsPricingCurrency(ieoId);
        if(Objects.isNull(byIsPricingCurrency)){
            return ReturnResult.fail(getInternational(language,InternationalCode.NOT_FOUND));
        }

        String requestCurrency = currencyRequest.getCurrency();
        if(StringUtils.isNoneBlank(requestCurrency) && !ieoCurrencyConfig.getCurrency().equals(requestCurrency)){
            return ReturnResult.fail(getInternational(language,InternationalCode.CURRENCY_CANNOT_CHANGED));
        }

        return modifyCurrencyCheck(currencyRequest, byIsPricingCurrency, currencyId, language);
    }

    /**
     * @Description: 修改币种
     * @Author: ChenXi
     * @Date:  2023/4/25 15:46
     * @param currencyRequest 请求参数
     * @param pricingCurrencyConfig 当前配置的计价币种配置
     * @param currencyId 请求修改的配置ID
     * @param language 语言
     * @return: com.novax.ex.common.results.ReturnResult<?>
     **/
    @Transactional(rollbackFor = Exception.class)
    public ReturnResult<?> modifyCurrencyCheck(IeoCurrencyRequest currencyRequest, IeoCurrencyConfig pricingCurrencyConfig,
                                                Long currencyId, String language) {
        Integer requestPricing = currencyRequest.getIsPricingCurrency();
        String requestCurrency = currencyRequest.getCurrency();
        String pricingCurrency = pricingCurrencyConfig.getCurrency();

        if(requestPricing.equals(1)){
            // request币种和已配置的计价币种一致
            if (!requestCurrency.equals(pricingCurrency)) {
                // request币种和已配置的计价币种不一致
                pricingCurrencyConfig.setIsPricingCurrency(0);
                ieoCurrencyConfigService.modifySelective(pricingCurrencyConfig);
            }
            return updateCurrency(currencyRequest, currencyId, language);

        }else if(requestPricing.equals(0)){
            // request币种和已配置的计价币种一致
            if(requestCurrency.equals(pricingCurrency)){
                // 至少得有一种币种配置为计价币种(前台展示)
                return ReturnResult.fail(getInternational(language, InternationalCode.PRICE_CURRENCY_UNIQUE_REQUIRED));
            } else {
                // request币种和已配置的计价币种不一致
                return updateCurrency(currencyRequest, currencyId, language);
            }
        }

        return ReturnResult.fail(getInternational(language, InternationalCode.REQUIREMENT));

    }

    private ReturnResult<?> updateCurrency(IeoCurrencyRequest currencyRequest, Long currencyId, String language) {

        // 币种转小写
        currencyRequest.setCurrency(currencyToLowerCase(currencyRequest.getCurrency()));
        IeoCurrencyConfig ieoCurrencyConfig = CopyUtils.copyObject(currencyRequest, IeoCurrencyConfig.class);
        ieoCurrencyConfig.setId(currencyId);
        ieoCurrencyConfig.setUpdateTime(new Date());
        boolean res = ieoCurrencyConfigService.modifySelective(ieoCurrencyConfig);
        return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
            : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    @Override
    public ReturnResult<?> removeCurrencyConfig(String language, Long currencyId) {
        if (Objects.isNull(currencyId)) {
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }

        // 计价币种不可删除
        IeoCurrencyConfig ieoCurrencyConfig = ieoCurrencyConfigService.find(currencyId);
        if(Objects.isNull(ieoCurrencyConfig) || ieoCurrencyConfig.getIsPricingCurrency().equals(1)){
            return ReturnResult.fail(getInternational(language, InternationalCode.PRICE_CURRENCY_UNIQUE_REQUIRED));
        }
        List<IeoCurrencyConfig> ieoCurrencyConfigs = ieoCurrencyConfigService.selectListByIeoId(ieoCurrencyConfig.getIeoId());
        // 一个IEO项目至少保留一个币种配置
        if(ieoCurrencyConfigs.size() == 1 ){
            return ReturnResult.fail(getInternational(language, InternationalCode.MIN_CONFIG));
        }

        boolean res = ieoCurrencyConfigService.remove(currencyId);
        return res ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    @Override
    public ReturnResult<List<IeoContentResponse>> contents(Long ieoId) {
        if (Objects.isNull(ieoId)) {
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }
        List<IeoContent> list = ieoContentService.findByIeoId(ieoId);
        return ReturnResult.success(CopyUtils.copyList(list, IeoContentResponse.class));
    }

    @Override
    public ReturnResult<Void> addContent(String language, Long ieoId, IeoContentRequest contentRequest, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }
        IeoContent ieoContent = ieoContentService.findByIeoIdAndLanguage(ieoId, contentRequest.getLanguage());
        if (Objects.nonNull(ieoContent)) {
            return ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
        }
        IeoContent content = CopyUtils.copyObject(contentRequest, IeoContent.class);
        content.setIeoId(ieoId);
        boolean result = ieoContentService.add(content);
        return result ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.ADMIN_IDENTICAL_DATA));
    }

    @Override
    public ReturnResult modifyContent(String language, Long contentId, IeoContentRequest contentRequest, BindingResult bindingResult) {
        if(bindingResult.hasErrors()){
            return ReturnResult.fail(InternationalCode.LACK_VALUE);
        }
        IeoContent ieoContent = CopyUtils.copyObject(contentRequest,IeoContent.class);
        ieoContent.setId(contentId);
        boolean result = ieoContentService.modifySelective(ieoContent);
        return result ? ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS))
                : ReturnResult.fail(getInternational(language, InternationalCode.OPERATION_ERROR));
    }

    @Override
    public ReturnResult<IeoSubscribeCopyResponse> realSubscribeCopy(String language, Long ieoId) {
        IeoSubscribeCopyResponse order = ieoOrderService.getOrder(ieoId);
        return ReturnResult.success(getInternational(language, InternationalCode.OPERATION_SUCCESS), order);
    }

    @Override
    public ReturnResult<PageUtil<IeoSubscribeResponse>> subscribePage(OrderInnerQuery innerQuery) {
        String[] userIdStrList = Optional.ofNullable(innerQuery.getUserIdListString())
            .filter(StringUtils::isNoneBlank)
            .map(s -> s.split(","))
            .orElseGet(() -> new String[0]);
        if(userIdStrList.length > 0){
            innerQuery.setUserIdList(Arrays.stream(userIdStrList).map(Long::parseLong).collect(Collectors.toList()));
        }
        PageUtil<IeoSubscribeResponse> pageInfo = new PageUtil<>(innerQuery);
        pageInfo.setCondition(Collections.singletonMap("query", innerQuery));
        ieoOrderService.findPage(pageInfo, IeoSubscribeResponse.class);
        return ReturnResult.success(pageInfo);
    }

    @Override
    public ReturnResult<Boolean> modifyMultiple(String language, Long ieoId, BigDecimal multiple) {
        IeoConfig ieoConfig = ieoConfigService.find(ieoId);
        BigDecimal resource = ieoConfig.getMultiple() == null ? BigDecimal.ZERO : ieoConfig.getMultiple();
        if (multiple.compareTo(resource) < 0) {
            return ReturnResult.fail(getInternational(language, InternationalCode.NO_LOWER_MULTIPLE));
        }
        ieoConfig.setMultiple(multiple);
        boolean result = ieoConfigService.modifySelective(ieoConfig);
        return ReturnResult.success(result);
    }

}
